/**
 * @file port_tim.c
 * @author yejiahao (yejiahao@pthyidh.com)
 * @brief
 * @version 0.1
 * @date 2025-01-13
 *
 * @copyright Copyright (c) 2024 by 深圳市鹏天微智能科技有限公司, All Rights Reserved.
 *
 */

/* ==================== [Includes] ========================================== */

#include "xf_hal_port.h"

#if XF_HAL_TIM_IS_ENABLE

#include <stdio.h>
#include <stdlib.h>

#include "pt3220.h"
#include "drvs.h"

#include "xf_utils.h"
#include "xf_init.h"
#include "xf_task.h"

/* ==================== [Defines] =========================================== */

#define XF_HAL_TIM_DEFAULT_TICK_FREQ_HZ 1000*100
#define XF_HAL_TIM_DEFAULT_COUNT_DIR    XF_HAL_TIM_COUNT_DIR_UP
#define XF_HAL_TIM_DEFAULT_ACTIVE       false
#define XF_HAL_TIM_DEFAULT_CALLBACK     NULL
#define XF_HAL_TIM_DEFAULT_USER_DATA    NULL

#define TAG "PORT_TMR"

#define PORT_TIMER_TOPIC_ID  (uint32_t)(-2)

/* ==================== [Typedefs] ========================================== */

typedef struct _port_tim_t {
    uint32_t port;
    xf_hal_tim_callback_t *isr;
    uint32_t count;
} port_tim_t;

typedef enum _tim_port_t {
    USER_TIM_BASIC,          /*!< 基本定时器 */
    USER_TIM_COMMON,         /*!< 通用定时器 */
    USER_TIM_ADVANCED,       /*!< 高级定时器 */
    USER_TIM_MAX
} tim_port_t;

/* ==================== [Static Prototypes] ================================= */

/* ==================== [Static Variables] ================================== */

static port_tim_t _timers[USER_TIM_MAX] = {0};

/* ==================== [Macros] ============================================ */

/* ==================== [Global Functions] ================================== */

void CTMR_IRQHandler(void)
{
    ctmr_intr_disable(TMR_IR_UI_BIT);

    if (ctmr_intr_mask_get() & TMR_IR_UI_BIT) {
        tim_port_t port = USER_TIM_COMMON;
        _timers[port].count = ctmr_get_counter();
        // Clear Interrupt Flag
        ctmr_intr_clr(TMR_IR_UI_BIT);
        if ((ctmr_intr_mask_get() & TMR_IR_UI_BIT) && _timers[port].isr && _timers[USER_TIM_COMMON].isr->callback) {
            _timers[port].isr->callback(_timers[port].port, _timers[port].count, _timers[port].isr->user_data);
        }

        xf_task_mbus_pub_async(PORT_TIMER_TOPIC_ID, (void *)&port);
    }

    // Enable UI Interrupt
    ctmr_intr_enable(TMR_IR_UI_BIT);

}

void ATMR_IRQHandler(void)
{
    atmr_intr_disable(TMR_IR_UI_BIT);

    if (atmr_intr_mask_get() & TMR_IR_UI_BIT) {

        tim_port_t port = USER_TIM_ADVANCED;
        _timers[port].count = atmr_get_counter();
        // Clear Interrupt Flag
        atmr_intr_clr(TMR_IR_UI_BIT);
        if ((atmr_intr_mask_get() & TMR_IR_UI_BIT) && _timers[port].isr
                && _timers[port].isr->callback) {
            _timers[port].isr->callback(_timers[port].port, _timers[port].count, _timers[port].isr->user_data);
        }

        xf_task_mbus_pub_async(PORT_TIMER_TOPIC_ID, (void *)&port);
    }

    // Enable UI Interrupt
    atmr_intr_enable(TMR_IR_UI_BIT);
}


/* ==================== [Static Functions] ================================== */

static void xf_isr_task_sub(const void *const data, void *user_data)
{
    tim_port_t port = *(tim_port_t *)data;
    xf_hal_tim_callback_t *cb = (xf_hal_tim_callback_t *)user_data;
    if (cb->callback) {
        cb->callback(_timers[port].port, _timers[port].count, cb->user_data);
    }
}

static void _tim_deinit(uint32_t tim_port)
{
    switch (tim_port) {
    case USER_TIM_ADVANCED:
        RCC_APBCLK_DIS(APB_ATMR_BIT);
        RCC_APBRST_REQ(APB_ATMR_BIT);
        break;
    case USER_TIM_COMMON:
        RCC_APBCLK_DIS(APB_CTMR_BIT);
        RCC_APBRST_REQ(APB_CTMR_BIT);
        break;
    case USER_TIM_BASIC:
        break;
    }
    XF_LOGD(TAG, "\nTimer deinitialized: Port %d\n", tim_port);
}

static int port_tim_open(xf_hal_dev_t *dev)
{
    if (dev->id == 1) {
        _timers[1].port = USER_TIM_COMMON;
        dev->platform_data = &_timers[1];
        return XF_OK;
    } else if (dev->id == 2) {
        _timers[2].port = USER_TIM_ADVANCED;

        dev->platform_data = &_timers[2];
        return XF_OK;
    }
    XF_LOGE(TAG, "\nTimer not support this port %d\n", dev->id);
    return XF_ERR_INVALID_ARG;
}

static int port_tim_ioctl(xf_hal_dev_t *dev, uint32_t cmd, void *config)
{
    xf_hal_tim_config_t *tim_config = (xf_hal_tim_config_t *)config;
    port_tim_t *tim = (port_tim_t *)dev->platform_data;

    if (cmd == XF_HAL_TIM_CMD_DEFAULT) {
        tim_config->tick_freq_hz = XF_HAL_TIM_DEFAULT_TICK_FREQ_HZ;
        tim_config->count_dir = XF_HAL_TIM_DEFAULT_COUNT_DIR;
        tim_config->active = XF_HAL_TIM_DEFAULT_ACTIVE;
        tim_config->cb.callback = XF_HAL_TIM_DEFAULT_CALLBACK;
        tim_config->isr.callback = XF_HAL_TIM_DEFAULT_CALLBACK;
        tim_config->cb.user_data = XF_HAL_TIM_DEFAULT_USER_DATA;
        tim_config->isr.user_data = XF_HAL_TIM_DEFAULT_USER_DATA;
        tim->isr = &tim_config->isr;
    }

    if (cmd == XF_HAL_TIM_CMD_ALL) {
        return XF_OK;
    }

    if (cmd == XF_HAL_TIM_CMD_TICK_FREQ_HZ)
    {
        if (tim_config->tick_freq_hz > 65535)
        {
            tim_config->tick_freq_hz = 0;
            return XF_ERR_INVALID_ARG;
        }
    }

    if (cmd == XF_HAL_TIM_CMD_TICK_FREQ_HZ)
    {
        if (tim_config->target_ticks > 65535)
        {
            tim_config->target_ticks = 0;
            return XF_ERR_INVALID_ARG;
        }
    }

    if (cmd & XF_HAL_TIM_CMD_ACTIVE) {
        if (tim_config->active) {
            uint16_t psc = (SYS_GET_CLOCK() / tim_config->tick_freq_hz) - 1;
            uint16_t arr = tim_config->target_ticks - 1;
            xf_task_mbus_sub(PORT_TIMER_TOPIC_ID, xf_isr_task_sub, &tim_config->cb);
            if (tim->port == USER_TIM_ADVANCED) {
                atmr_init(psc, arr);
                atmr_set_counter_dir(tim_config->count_dir);
                if (tim_config->auto_reload) {
                    atmr_ctrl(TMR_PERIOD_MODE, TMR_IR_UI_BIT);
                } else {
                    atmr_ctrl(TMR_1PULSE_MODE, TMR_IR_UI_BIT);
                }
                atmr_intr_clr(TMR_IR_UI_BIT);
                NVIC_EnableIRQ(ATMR_IRQn);
                XF_LOGI(TAG, "active");
            } else if (tim->port == USER_TIM_COMMON) {
                ctmr_init(psc, arr);
                XF_LOGI(TAG, "psc:%d", psc);
                XF_LOGI(TAG, "arr:%d", arr);
                ctmr_set_counter_dir(tim_config->count_dir);
                if (tim_config->auto_reload) {
                    ctmr_ctrl(TMR_PERIOD_MODE, TMR_IR_UI_BIT);
                } else {
                    ctmr_ctrl(TMR_1PULSE_MODE, TMR_IR_UI_BIT);
                }
                ctmr_intr_clr(TMR_IR_UI_BIT);
                NVIC_EnableIRQ(CTMR_IRQn);
                XF_LOGI(TAG, "CTMR active");
            }
        } else {
            if (tim->port == USER_TIM_ADVANCED) {
                atmr_counter_enable(false);
                NVIC_DisableIRQ(ATMR_IRQn);
                atmr_deinit();
            } else if (tim->port == USER_TIM_COMMON) {
                ctmr_counter_enable(false);
                NVIC_DisableIRQ(CTMR_IRQn);
                ctmr_deinit();
            }
        }
    }

    return 0;
}

static int port_tim_read(xf_hal_dev_t *dev, void *buf, size_t count)
{
    port_tim_t *tim = (port_tim_t *)dev->platform_data;

    if (tim->port == USER_TIM_COMMON) {
        *(uint32_t *)buf = ctmr_get_counter();
    } else if (tim->port == USER_TIM_ADVANCED) {
        *(uint32_t *)buf = atmr_get_counter();
    }
    return 1;
}

static int port_tim_write(xf_hal_dev_t *dev, const void *buf, size_t count)
{
    port_tim_t *tim = (port_tim_t *)dev->platform_data;
    if (tim->port == USER_TIM_COMMON) {
        ctmr_set_counter(*(uint32_t *)buf);
    } else if (tim->port == USER_TIM_ADVANCED) {
        atmr_set_counter(*(uint32_t *)buf);
    }
    return 1;
}

static int port_tim_close(xf_hal_dev_t *dev)
{
    port_tim_t *tim = (port_tim_t *)dev->platform_data;
    _tim_deinit(tim->port);
    for (int i = 0; i < USER_TIM_MAX; i++) {
        if (_timers[i].port == tim->port) {
            _timers[i].port = 0;
            _timers[i].isr = NULL;
        }
    }
    return 0;
}

static int xf_hal_TIM_reg(void)
{
    xf_driver_ops_t ops = {
        .open = port_tim_open,
        .ioctl = port_tim_ioctl,
        .write = port_tim_write,
        .read = port_tim_read,
        .close = port_tim_close,
    };
    xf_hal_tim_register(&ops);
    xf_task_mbus_reg_topic(PORT_TIMER_TOPIC_ID, sizeof(tim_port_t));
    return XF_OK;
}

XF_INIT_EXPORT_PREV(xf_hal_TIM_reg);

#endif
